﻿using System;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Xml.Serialization;
using System.Xml;
using System.Collections.Generic;

namespace RedmineIssuesProvider
{

    public class Filter
    {
        [XmlAttribute]
        public string name;

        public override string ToString()
        {
            return name;
        }

        #region Фильтр по IssueStatus
        public bool statusChecked = false;
        public OpenClosed statusCondition;
        public IssueStatus status;

        public void SetStatus(OpenClosed _statusCondition, IssueStatus _status)
        {
            statusChecked = true;
            statusCondition = _statusCondition;
            status = _status;
        }

        private bool MatchStatus(Issue issue)
        {
            bool result = true;

            if (statusChecked)
            {
                switch (statusCondition)
                {
                    case OpenClosed.Open:
                        result = (!issue.Closed());
                        break;
                    case OpenClosed.Closed:
                        result = (issue.Closed());
                        break;
                    case OpenClosed.Equal:
                        result = (issue.status == status);
                        break;
                    case OpenClosed.NotEqual:
                        result = (issue.status != status);
                        break;
                }
            }

            return result;
        }
        #endregion

        #region Фильтр по Tracker
        public bool trackerChecked = false;
        public EqualNotEqual trackerCondition;
        public Tracker tracker;

        public void SetTracker(EqualNotEqual _trackerCondition, Tracker _tracker)
        {
            trackerChecked = true;
            trackerCondition = _trackerCondition;
            tracker = _tracker;
        }

        private bool MatchTracker(Issue issue)
        {
            bool result = true;

            if (trackerChecked)
            {
                switch (trackerCondition)
                {
                    case EqualNotEqual.Equal:
                        result = (issue.tracker == tracker);
                        break;
                    case EqualNotEqual.NotEqual:
                        result = (issue.tracker != tracker);
                        break;
                }
            }

            return result;
        }
        #endregion

        #region Фильтр по Priority
        public bool priorityChecked = false;
        public EqualNotEqual priorityCondition;
        public Priority priority;

        public void SetPriority(EqualNotEqual _priorityCondition, Priority _priority)
        {
            priorityChecked = true;
            priorityCondition = _priorityCondition;
            priority = _priority;
        }

        private bool MatchPriority(Issue issue)
        {
            bool result = true;

            if (priorityChecked)
            {
                switch (priorityCondition)
                {
                    case EqualNotEqual.Equal:
                        result = (issue.priority == priority);
                        break;
                    case EqualNotEqual.NotEqual:
                        result = (issue.priority != priority);
                        break;
                }
            }

            return result;
        }
        #endregion

        #region Фильтр по Author
        public bool authorChecked = false;
        public EqualNotEqual authorCondition;
        public User author;

        public void SetAuthor(EqualNotEqual _authorCondition, User _author)
        {
            authorChecked = true;
            authorCondition = _authorCondition;
            author = _author;
        }

        private bool MatchAuthor(Issue issue)
        {
            bool result = true;

            if (authorChecked)
            {
                switch (authorCondition)
                {
                    case EqualNotEqual.Equal:
                        result = (issue.author == author);
                        break;
                    case EqualNotEqual.NotEqual:
                        result = (issue.author != author);
                        break;
                }
            }

            return result;
        }
        #endregion

        #region Фильтр по Assigned
        public bool assignedChecked = false;
        public AssignedCondition assignedCondition;
        public User assigned;

        public void SetAssigned(AssignedCondition _assignedCondition, User _assigned)
        {
            assignedChecked = true;
            assignedCondition = _assignedCondition;
            assigned = _assigned;
        }

        private bool MatchAssigned(Issue issue)
        {
            bool result = true;

            if (assignedChecked)
            {
                switch (assignedCondition)
                {
                    case AssignedCondition.Me:
                        result = (issue.assigned_to != null && Properties.Settings.Default.CurrentUserID == issue.assigned_to.id);
                        break;
                    case AssignedCondition.None:
                        result = (issue.assigned_to == null);
                        break;
                    case AssignedCondition.Equal:
                        result = (issue.assigned_to == assigned);
                        break;
                    case AssignedCondition.NotEqual:
                        result = (issue.assigned_to != assigned);
                        break;
                }
            }

            return result;
        }
        #endregion

        #region Фильтр по DueDate
        public bool dueDateChecked = false;
        public DueDateCondition dueDateCondition;
        public int Days;

        public void SetDueDate(DueDateCondition _dueDateCondition, int _days)
        {
            dueDateChecked = true;
            dueDateCondition = _dueDateCondition;
            Days = _days;
        }

        private bool MatchDueDate(Issue issue, DateTime today)
        {
            bool result = true;

            if (dueDateChecked)
            {
                TimeSpan diff;
                switch (dueDateCondition)
                {
                    case DueDateCondition.Less:
                        diff = issue.due_date - today;
                        result = (diff.Days < Days && diff.Days > 0);
                        break;
                    case DueDateCondition.More:
                        diff = issue.due_date - today;
                        result = (diff.Days > Days);
                        break;
                    case DueDateCondition.Equal:
                        diff = issue.due_date - today;
                        result = (diff.Days == Days);
                        break;
                    case DueDateCondition.Today:
                        result = (issue.due_date.Date == today);
                        break;
                    case DueDateCondition.ThisWeek:
                        Calendar myC = new GregorianCalendar();
                        result = ((myC.GetWeekOfYear(issue.due_date, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday) == myC.GetWeekOfYear(today, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday)) && today.Year == issue.due_date.Year);
                        break;
                    case DueDateCondition.LessAgo:
                        diff = today - issue.due_date;
                        result = (diff.Days < Days && diff.Days > 0);
                        break;
                    case DueDateCondition.MoreAgo:
                        diff = today - issue.due_date;
                        result = (diff.Days > Days);
                        break;
                    case DueDateCondition.EqualAgo:
                        diff = today - issue.due_date;
                        result = (diff.Days == Days);
                        break;
                }
            }

            return result;
        }
        #endregion

        public bool Match(Issue issue, DateTime today)
        {
            return MatchStatus(issue) && MatchTracker(issue) && MatchPriority(issue) && MatchAuthor(issue) && MatchAssigned(issue) && MatchDueDate(issue,today);
        }

        #region Сериализация

        public void Save()
        {
            XDocument doc = XDocument.Load(Properties.Settings.Default.FiltersFileName);

            if (doc.Root != null)
            {
                IEnumerable<XElement> filters =
                    from item in doc.Root.Descendants("Filter")
                    where item.Attribute("name").Value == name
                    select item;

                XmlSerializer sr = new XmlSerializer(typeof(Filter));
                StringBuilder sb = new StringBuilder(null);
                StringWriter sw = new StringWriter(sb);

                if (filters.Count() > 0)
                {
                    XElement currentFilter = filters.First();
                    sr.Serialize(sw, this);
                    currentFilter.ReplaceWith(XElement.Parse(sb.ToString()));
                }
                else
                {
                    sr.Serialize(sw, this);
                    XElement newElement = XElement.Parse(sb.ToString());
                    doc.Root.Add(newElement);
                }
                doc.Save(Properties.Settings.Default.FiltersFileName);
            }
        }

        #endregion
    }

    public class FilterList
    {
        [XmlArray]
        [XmlArrayItem(typeof(Filter))]
        public List<Filter> Content;

        public FilterList()
        {
            Content = new List<Filter>();
        }

        public void GetAll(string fileName)
        {
            Content.Clear();

            if (!File.Exists(fileName))
            {
                XDocument newDoc = new XDocument(
                    new XElement("Filters"));
                newDoc.Save(fileName);
            }

            XDocument doc = XDocument.Load(fileName);

            if (doc.Root != null)
            {
                IEnumerable<XElement> filters =
                    from item in doc.Root.Descendants("Filter")
                    select item;

                XmlSerializer sr = new XmlSerializer(typeof(Filter));

                foreach (XElement filterElement in filters)
                {
                    StringReader reader = new StringReader(filterElement.ToString());

                    Filter newFilter = (Filter)sr.Deserialize(reader);

                    Content.Add(newFilter);
                }
            }
        }
    }
}